package com.cilico.single.utils;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.HttpURLConnection;
import java.net.URL;
import com.cilico.single.ConfigManager;
import com.cilico.single.Mid;
import com.cilico.single.components.WaitDialog;
import com.cilico.single.view.BaseView;
import com.cilico.single.waterfall.Waterfall;

/**
 * HTTP管理器
 * @author lv.xiangan
 *
 */
public final class HttpManager {
	final static String POST = "POST";
	final static String GET = "GET";
	// private ApnGateway apn_;
	protected static final String MIME_ARC = "application/octet-stream";
	private final Mid mid_;
	public static int connectType_; // 0-CMNET; 1-CMWAP.
	boolean isGetWML_ = false;
	final String cancel_ = "取消";

	String errorStr;
	public String errorType;
	public String errorContent;

	// XXX This belongs to a Util package.
	@SuppressWarnings("serial")
	public static final class EmptyResultException extends Exception {
		public EmptyResultException(String s) {
			super(s);
		}
	}

	@SuppressWarnings("serial")
	public
	static final class NoGatewayException extends Exception {
		public NoGatewayException(String s) {
			super(s);
		}
	}

	// When we do cancel throw this exception.
	@SuppressWarnings("serial")
	public static final class CancelIOException extends IOException {
		public CancelIOException(String s) {
			super(s);
		}
	}

	public HttpManager(Mid mid) {
		mid_ = mid;
	}

	public String sendRequest(String url, WaitDialog.Task task) throws Exception, NoGatewayException {
		Utils.printOutToConsole("url-->" + url);
		isGetWML_ = false;
		if (url.indexOf("?") == -1) {
			url = url.concat("?");
		}
//		if (url.endsWith("?"))
//			url = url.concat("app=gdb");
//		else
//			url = url.concat("&app=gdb");
//		url = url.concat("&o=i");
		Object resultObj = sendHttpRequest(url, null, GET, null, null, task);
		String result = null;
		if (resultObj != null && resultObj instanceof String) {
			result = (String) resultObj;
		}
		return result;
	}

	/**
	 * @param url
	 * @param requestBody
	 * @param task
	 * @param contentType
	 *            TODO
	 * @param accept
	 *            TODO
	 * @return
	 * @throws Exception
	 * @throws NoGatewayException
	 */
	public Object sendPostRequest(String url, Object requestBody, WaitDialog.Task task, String contentType, String accept) throws NoGatewayException, Exception {
		isGetWML_ = false;
		if (url.indexOf("?") == -1)
			url = url.concat("?");
//		if (url.endsWith("?")){
//			url = url.concat("app=cilico");
//		}else{
//			url = url.concat("&app=cilico");
//		}
//		url = url.concat("&platform=a");
		Object resultObj = null;
		if (requestBody == null) {
			resultObj = sendHttpRequest(url, null, GET, null, null, task);
		} else {
			resultObj = sendHttpRequest(url, requestBody, POST, contentType, accept, task);
		}
		return resultObj;
	}

	/**
	 * @param url
	 * @param requestStr
	 * @param task
	 * @return
	 * @throws Exception
	 * @throws NoGatewayException
	 */
	String sendPostRequestWML(String url, String requestStr, WaitDialog.Task task) throws Exception, NoGatewayException {
		Utils.printOutToConsole("url-->" + url);
		isGetWML_ = true;
		if (url.indexOf("?") == -1) {
			url = url.concat("?");
		}
		if (url.endsWith("?"))
			url = url.concat("app=gdb");
		else
			url = url.concat("&app=gdb");

		url = url.concat("&o=i");

		Object resultObj = null;
		String wml = null;
		resultObj = sendHttpRequest(url, requestStr, POST, null, null, task);
		if (resultObj != null && resultObj instanceof String) {
			wml = (String) resultObj;
			if (wml != null && wml.indexOf("name=\"SIGN\"") == -1) {
				// 没有手机签名字段。
				resultObj = sendHttpRequest(url, requestStr, POST, null, null, task);
				if (resultObj != null && resultObj instanceof String) {
					wml = (String) resultObj;
				}
			}
			isGetWML_ = false;
		}
		return wml;
	}

	/**
	 * @param url
	 * @param requestBody
	 * @param method
	 * @param contentType
	 * @param accept
	 * @param task
	 * @return
	 * @throws Exception
	 * @throws NoGatewayException
	 */
	private Object sendHttpRequest(String url, Object requestBody, String method, 
			String contentType, String accept, WaitDialog.Task task) 
					throws Exception, NoGatewayException {
		Utils.printOutToConsole(requestBody);
		HttpURLConnection hc = null;
		OutputStream dos = null;
		InputStream dis = null;
		Object resultObj = null;
		String result = null;
		errorStr = null;
		errorType = null;
		errorContent = null;
		//所有的请求都带上用户ID
		if (url.endsWith("?") || url.endsWith("&")){
			url = url.concat("restaurantUserId="+BaseView.mid_.pm_.SAVEPROPERTYDB_.get("userId"));
		}else{
			url = url.concat("&restaurantUserId="+BaseView.mid_.pm_.SAVEPROPERTYDB_.get("userId"));
		}
		Utils.printOutToConsole("sendHttpRequest--url--->" + url);
		try {
			// Open up a http connection with the Web server for both send and receive operations
			if (task != null && task.isStop())
				throw new CancelIOException(cancel_);
			String ct = null;
			int respCode = 0;
			// 这里最多尝试两次，两次后即使还是移动收费提示页我们也返回。对于取wml内容的, 此处不处理，取完内容后再处理。
			// 在这里不比较是否是使用cmwap接入点，考虑到有些手机使用直连也能通过cmwap, 但却得到收费提示页。
			int times = 2;
			do {
				if (times != 2)
					Utils.printOutToConsole("得到收费提示页");
				times--;
				try {
					if (dos != null)
						dos.close();
					if (hc != null)
						hc.disconnect();
				} catch (IOException ignored) {
					Utils.printException(ignored);
				}
				hc = getOpenedConnection(url, method, contentType, accept, requestBody);

				if (task != null && task.isStop())
					throw new CancelIOException(cancel_);
				// Send the string entered by user byte by byte
				if (requestBody != null) {
					byte[] body = null;
					if (requestBody instanceof String) {
						body = ((String) requestBody).getBytes("UTF-8");
					} else {
						body = (byte[]) requestBody;
					}
					hc.setRequestProperty("Content-Length", "" + body.length);
					// 允许output操作
					hc.setDoOutput(true);
					dos = hc.getOutputStream();
					dos.write(body);
				} else {
					hc.setRequestProperty("Content-Length", "0");
				}
				if (task != null && task.isStop()) {
					throw new CancelIOException(cancel_);
				}
				hc.setConnectTimeout(3000);
				try {
					respCode = hc.getResponseCode();// 40s超时
				} catch (java.io.IOException e) {// 防止由于tcp层网络异常引起的Theconnection was reset问题
					throw new NoGatewayException("网络异常，请重试！");
				}
				Utils.printOutToConsole("ResponseCode--->" + respCode);
				ct = hc.getHeaderField("Content-Type");
				if (respCode != HttpURLConnection.HTTP_OK && ((respCode != 408 && respCode != 302 && respCode != 100) || times <= 0)) {
					// throw new NoGatewayException("无法连接服务器！");
					throw new NoGatewayException(Constant.ERR_CONNECTION_FAIL);
				}
			} while (times > 0 && !isGetWML_ && ((respCode == 408 || respCode == 302 || respCode == 100)) || ct != null && (ct.indexOf("text/vnd.wap.wml") != -1 || ct.indexOf("application/vnd.wap.wmlc") != -1));

			// 允许使用input
			// hc.setDoInput(true);待议，该方法同一个对象只要调用一次即可，调用多次会引发异常。
			dis = hc.getInputStream();
			long len = hc.getContentLength(); // we can't use dis.available().
			if (task != null) {
				task.setTotalSize((int) len);
			}
			ByteArrayOutputStream buffer = new ByteArrayOutputStream(1024);
			readByByte(dis, len, buffer, task);

			byte[] resultByts = buffer.toByteArray();
			buffer.close();
			// unZip the result.
			String gzip = hc.getHeaderField("Content-Encoding");
			if (gzip == null) {
				gzip = "not gzip";
			}
			if (gzip != null && gzip.trim().equals("gzip")) {
				try {
					resultByts = Utils.gunzip(resultByts);
				} catch (ArrayIndexOutOfBoundsException e) {
				}
			}

			if (ct.equals(MIME_ARC)) {
				resultObj = resultByts;
			} else {
				result = new String(resultByts, "UTF-8");
				Utils.printOutToConsole("sendHttpRequest()-->result-------->" + result);
				if (result.indexOf("<error") != -1) {
					String error = Utils.getXMLResponseAttribute(result, "string=\"", 0, Utils.MATCH);
					errorStr = error;
					errorType = Utils.getXMLResponseAttribute(result, "type=\"", 0, Utils.MATCH);
					errorContent = Utils.getXMLResponseAttribute(result, "content=\"", 0, Utils.MATCH);
					throw new Exception(error);
				} else {
					int index = result.indexOf("<localdiv");
					if (index != -1) {
						String r1 = result.substring(0, index);
						int index1 = result.indexOf('>', index) + 1;
						String r2 = result.substring(index1, result.length());
						result = r1 + r2;
					}
				}
				resultObj = result;
			}
			if (task != null && task.isStop()) {
				throw new CancelIOException(cancel_);
			}
		} catch (CancelIOException cancel) {
			throw cancel;
		} catch (NoGatewayException noGateway) {
			throw noGateway;
		} catch (SecurityException se) {
			String temp = "通信异常，请检查您的网络设置及通讯情况，同时确认您的手机是否允许使用网络!";
			// BaseView.mid_.alert(ConfigManager.currentView_, temp, false);
			throw new NoGatewayException(temp);
		} catch (ConnectException cf) {
			throw new IOException("网络繁忙，请稍后重试!");
		} catch (Exception ioe) {
			Utils.printException(ioe);
			String temp = "";
			if (ioe.toString().indexOf("通信异常") != -1) {
				temp = "通信异常，请检查您的网络设置及通讯情况!";
			} else if (!ioe.getMessage().startsWith("alert:") && ioe.getMessage().contains("cs.cgbchina.com.cn")) {
				temp = "通信异常，请稍后再试!";
			} else {
				temp = ioe.getMessage();
			}
			throw new IOException(temp);
		} catch (Error err) {
			Utils.printOutToConsole("HttpManager-sendHttpRequest-err:" + err.toString());
			String temp = "系统资源不足，退出重新登录可缓解这个问题.";
			mid_.alert(ConfigManager.currentView_, temp, false);
			throw new Exception(temp);
		} finally {
			// Free up i/o streams and http connection 
			try {
				// if (dir != null) dir.close();
				if (dis != null)
					dis.close();
				if (dos != null)
					dos.close();
				if (hc != null)
					hc.disconnect();
			} catch (IOException ignored) {
			}
		}
		if (null != result && result.indexOf("<error") != -1) {
			String error = Utils.getXMLResponseAttribute(result, "string=\"", 0, Utils.MATCH);
			errorStr = error;
			errorType = Utils.getXMLResponseAttribute(result, "type=\"", 0, Utils.MATCH);
			errorContent = Utils.getXMLResponseAttribute(result, "css_content=\"", 0, Utils.MATCH);
			throw new Exception(error);
		}
		return resultObj;
	}

	/**
	 * Allow caller to insert its own code during the HTTP downloading process by breaking connect/read/disconnect into
	 * pieces.
	 * 
	 * We support basic HTTP authentication, such as http://username:password@web.site
	 * 
	 * @param url
	 * @param method
	 * @param contentType
	 * @param accept
	 * @param requestBody
	 * @return
	 * @throws Exception
	 * @throws NoGatewayException
	 */
	HttpURLConnection getOpenedConnection(String url, final String method, final String contentType, final String accept, Object requestStr) throws NoGatewayException, Exception {
		// Parse URL to extract user name and password, if applicable.
		HttpURLConnection hc = null;
		URL connector = new URL(url);
		int idx1, idx2;
		String credential = null;
		if ((idx1 = url.indexOf('@')) != -1) {
			idx2 = url.indexOf("://");
			if (idx2 == -1)
				idx2 = 0;
			else
				idx2 += 3;
			credential = "Basic " + url.substring(idx2, idx1).getBytes();
			String realUrl = "http://";
			realUrl += url.substring(idx1 + 1);
			url = realUrl;
		}
		String host = null;
		if (connectType_ == 1) { // CMWAP
			host = url;
			int startIndex = host.indexOf("//") + 2;
			int endIndex = host.indexOf('/', startIndex);
			if (endIndex > 0)
				host = host.substring(startIndex, endIndex);
			int index = url.indexOf('/', startIndex);
			String query = url.substring(index);
			connector = new URL(Utils.wapHost.concat(query));
		}
		hc = (HttpURLConnection) connector.openConnection();
		if (credential != null)
			hc.setRequestProperty("Authorization", credential);
		if (connectType_ == 1) //CMWAP
			hc.setRequestProperty("X-Online-Host", host);
		hc.setRequestMethod(method);
		// Accept
		String apt = null;
		if (accept == null) {
			// request wml
			apt = "text/vnd.wap.wml";
		} else {
			apt = accept;
		}
		hc.setRequestProperty("Accept", apt);
		Utils.printOutToConsole("request Accept ---> " + apt);
		hc.setRequestProperty("User-Agent", ConfigManager.APP_USERAGENT);
		hc.setRequestProperty("Pragma", "no-cache");
		hc.setRequestProperty("Connection", "Keep-Alive");
		hc.setRequestProperty("Proxy-Connection", "Keep-Alive");
		// content type.
		if (method.equals(POST)) {
			// Don't need to set content-type for get.
			String ct = null;
			if (contentType == null) {
				ct = "application/x-www-form-urlencoded";
			} else {
				ct = contentType;
			}
			hc.setRequestProperty("Content-Type", ct);
			Utils.printOutToConsole("request Content-Type ---> " + ct);
		}
		if (requestStr != null) {
			hc.setRequestProperty("X-Emp-Signature", (String) requestStr);
		}
		return hc;
	}

	private void readByByte(InputStream dis, long len, ByteArrayOutputStream buffer, WaitDialog.Task task) throws IOException {
		int bufferSize = 512;
		byte byteInput[] = new byte[bufferSize];
		int size = 0;
		if (len != -1) {
			long askSize = 0;
			while (len > 0) {
				askSize = (len < (long) bufferSize) ? len : (long) bufferSize;
				if ((size = dis.read(byteInput, 0, (int) askSize)) != -1) {
					len -= size;
					if (task != null)
						task.updateFinish(size);
					buffer.write(byteInput, 0, size);
				} else {
					break;
				}
				if (task != null && task.stop_)
					throw new CancelIOException(cancel_);
			}
		} else {
			// if the content-length is not available
			while ((size = dis.read(byteInput, 0, bufferSize)) != -1) {
				buffer.write(byteInput, 0, size);
				if (task != null && task.stop_)
					throw new CancelIOException(cancel_);
			}
		}
	}

	/**
	 * Allow caller to insert its own code during the HTTP downloading process by breaking connect/read/disconnect into
	 * pieces.
	 * 
	 * We support basic HTTP authentication, such as http://username:password@web.site
	 */
	HttpURLConnection connect(String url, String requestStr, String method) throws Exception, NoGatewayException {
		Utils.printOutToConsole("connect--url--->" + url);
		if (requestStr != null)
			Utils.printOutToConsole("requestStr------>" + requestStr);
		// Parse URL to extract user name and password, if applicable.
		HttpURLConnection hc = null;
		URL connector = new URL(url);
		int idx1, idx2;
		String credential = null;
		if ((idx1 = url.indexOf('@')) != -1) {
			idx2 = url.indexOf("://");
			if (idx2 == -1)
				idx2 = 0;
			else
				idx2 += 3;
			credential = "Basic " + url.substring(idx2, idx1).getBytes();
			String realUrl = "http://";
			realUrl += url.substring(idx1 + 1);
			url = realUrl;
		}
		String host = null;
		if (connectType_ == 1) { // CMWAP
			host = url;
			int startIndex = host.indexOf("//") + 2;
			int endIndex = host.indexOf('/', startIndex);
			if (endIndex > 0)
				host = host.substring(startIndex, endIndex);
			int index = url.indexOf('/', startIndex);
			String query = url.substring(index);
			connector = new URL(Utils.wapHost.concat(query));
		}
		
		hc = (HttpURLConnection) connector.openConnection();
		
		if (credential != null)
			hc.setRequestProperty("Authorization", credential);
		if (connectType_ == 1) //CMWAP
			hc.setRequestProperty("X-Online-Host", host);
		hc.setRequestMethod(method);
		hc.setRequestProperty("Accept", "text/vnd.wap.wml"); // request wml
		// 由于wap会屏蔽掉User-Agent属性，导致客户端在使用神州行卡连接时不能顺利登录客户端，所以改成Android-Version属性解决该问题
		hc.setRequestProperty("Android-Version", ConfigManager.APP_USERAGENT);
		hc.setRequestProperty("Pragma", "no-cache");
		hc.setRequestProperty("Connection", "Keep-Alive");
		hc.setRequestProperty("Proxy-Connection", "Keep-Alive");
		if (method.equals(POST)) // Don't need to set content-type for get.
			// symbian plat have problem for this.
			hc.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
		if (requestStr != null) {
			hc.setRequestProperty("X-EMP-Signature", requestStr);
		}
		return hc;
	}

	public void read(String url, ByteArrayOutputStream buffer, WaitDialog.Task task) throws IOException, NoGatewayException {
		HttpURLConnection hc = null;
		InputStream dis = null;
		try {
			if (task != null && task.stop_)
				throw new CancelIOException(cancel_);
			String contentType = null;
			// 这里最多尝试两次，两次后即使还是移动收费提示页我们也返回。
			int times = 2;
			do {
				if (times != 2)
					Utils.printOutToConsole("得到收费提示页");
				times--;
				if (hc != null)
					hc.disconnect();
				hc = connect(url, null, GET);
				if (task != null && task.stop_)
					throw new CancelIOException(cancel_);
				int res = hc.getResponseCode();
				Utils.printOutToConsole("ResponseCode--->" + res);
				if (res != HttpURLConnection.HTTP_OK)
					throw new IOException("无法连接服务器！");
				contentType = hc.getHeaderField("Content-Type");
			} while (times > 0 && contentType != null && (contentType.indexOf("text/vnd.wap.wml") != -1 || contentType.indexOf("application/vnd.wap.wmlc") != -1));
			// Check the Content-Length first
			long len = hc.getContentLength();
			if (task != null)
				task.setTotalSize((int) len);
			// 允许使用Input
			if (!hc.getDoInput())
				hc.setDoInput(true);
			dis = hc.getInputStream();
			if (task != null && task.stop_)
				throw new CancelIOException(cancel_);
			readByByte(dis, len, buffer, task);
		} catch (CancelIOException cancel) {
			throw cancel;
		} catch (SecurityException se) {
			throw new IOException("通信异常，请检查您的网络设置及通讯情况，同时确认您的手机是否允许使用网络!");
		} catch (ConnectException cf) {
			throw new IOException("网络繁忙，请稍后重试!");
		} catch (Exception ioe) {
			Utils.printOutToConsole("HttpManager-read-ioe:" + ioe.toString());
			throw new IOException("通信异常，请检查您的网络设置及通讯情况!");
		} catch (Error err) {
			Utils.printOutToConsole("HttpManager-read-ioe:" + err.toString());
			throw new IOException("系统资源不足，退出重新登录可缓解这个问题.");
		} finally {
			try {
				if (dis != null)
					dis.close();
				if (hc != null)
					hc.disconnect();
			} catch (IOException ignored) {
				ignored.printStackTrace();
			}
		}
	}

	void closeConnection(HttpURLConnection hc) throws IOException {
		if (hc != null) {
			hc.disconnect();
		}
	}
	
	/**
	 * 瀑布流请求更多数据
	 * @author lvke
	 * Email: lvke@live.cn
	 * 2013-12-12下午3:42:45
	 * @param url
	 * @return
	 */
	public String requstData(Waterfall waterfall,String url,String orderBy){
		String data = null;
		StringBuffer body = new StringBuffer();
		isGetWML_ = false;
		if (url.indexOf("?") == -1)
			url = url.concat("?");
		if (!url.endsWith("?")){
			body.append("&");
		}
		body.append("targetPage=");
		body.append(waterfall.PAGE_NEXT);
		body.append("&pageSize=");
		body.append(Waterfall.PAGE_SIZE);
		
		body.append("&cmbNum=");
		body.append(waterfall.cmbNum_);
		
		if (orderBy != null && !orderBy.trim().equals("")) {
			body.append("&order_by=");
			body.append(orderBy);
		}
		Utils.printOutToConsole("requstData--url-->" + url);
		try {
			String bodyString = body.toString();
			data = (String) sendHttpRequest(url, bodyString, GET, null, null, null);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return data;
	}
	
}
